using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;

namespace DatabaseUnitTesting.Utilities
{
    internal enum Existence
    {
        No,
        Database,
        Snapshot
    } ;

    internal class DatabaseAdapter
    {
        private readonly SqlConnection _connection;

        internal DatabaseAdapter(SqlConnection connection)
        {
            _connection = connection;
            ValidateConnection();
        }

        internal void ValidateConnection()
        {
            if(_connection == null)
                throw new ArgumentNullException("SqlConnection cannot be null");
            if(_connection.DataSource == null)
                throw new ArgumentNullException("Datasource (server) not set in SqlConnection");
            if(_connection.Database == null)
                throw new ArgumentNullException("Database not set in SqlConnection.");
            if(_connection.State == ConnectionState.Broken)
            {
                _connection.Close();
                _connection.Open();
            }
            if(_connection.State == ConnectionState.Closed)
                _connection.Open();
        }

        internal Existence TestExistence(string name)
        {
            SqlCommand command = _connection.CreateCommand();
            command.CommandText = 
                "--Testing Existence Type\nSELECT source_database_id FROM sys.databases WHERE name = @name";

            command.Parameters.AddWithValue("@name", name);

            object objResult = command.ExecuteScalar();

            if(objResult == null)
                return Existence.No;
            else if(objResult is DBNull)
                return Existence.Database;
            else
                return Existence.Snapshot;
        }

        internal void UseDatabase(string databaseName)
        {
            if(TestExistence(databaseName) == Existence.No)
                throw new ArgumentException("A database of name " + databaseName + " does not exist.");
            
            SqlCommand command = _connection.CreateCommand();
            command.CommandText = "USE " + databaseName;
            command.ExecuteNonQuery();
        }

        internal void CreateSnapshot(string databaseName, string snapshotName)
        {
            if(TestExistence(databaseName) != Existence.Database)
                throw new ArgumentException("A database of name " + databaseName + " does not exist.");

            Existence snapshotExistence = TestExistence(snapshotName);

            if(snapshotExistence == Existence.Database)
                throw new ArgumentException("A snapshot cannot overwrite database with name " + snapshotName);

            else if(snapshotExistence == Existence.Snapshot)
                DropSnapshot(snapshotName);

            SqlCommand command = _connection.CreateCommand();
            
            StringBuilder stringBuilder = new StringBuilder("-- Creating Snapshot \nCREATE DATABASE ");
            stringBuilder.Append(snapshotName);
            stringBuilder.Append(" ON ( NAME = ");
            stringBuilder.Append(databaseName);
            stringBuilder.Append(", FILENAME = 'C:\\Temp\\");
            stringBuilder.Append(snapshotName);
            stringBuilder.Append("') AS SNAPSHOT OF ");
            stringBuilder.Append(databaseName);
            command.CommandText = stringBuilder.ToString();
            command.ExecuteNonQuery();
        }
     
        internal void DropSnapshot(string snapshotName)
        {
            ConnectionState state = _connection.State;
            if (state == ConnectionState.Closed)
                _connection.Open();
            
            if(TestExistence(snapshotName) != Existence.Snapshot)
                throw new ArgumentException("A snapshot of name " + snapshotName + " does not exist.");

            SqlCommand command = _connection.CreateCommand();
            command.CommandText = "DROP DATABASE " + snapshotName;
            command.ExecuteNonQuery();

            if (state == ConnectionState.Closed)
                _connection.Close();
        }
    }
}